package com.erlang4j.internal.messages;

import static com.erlang4j.internal.messages.MessageTokenType.atomName;
import static com.erlang4j.internal.messages.MessageTokenType.closeList;
import static com.erlang4j.internal.messages.MessageTokenType.closeTuple;
import static com.erlang4j.internal.messages.MessageTokenType.openList;
import static com.erlang4j.internal.messages.MessageTokenType.openTuple;
import static com.erlang4j.internal.messages.MessageTokenType.variable;

import java.util.Arrays;
import java.util.Stack;

import junit.framework.TestCase;

import com.erlang4j.api.exceptions.Erlang4jMalformedPattern;
import com.erlang4j.api.exceptions.Erlang4jUnexpectedEndOfTokens;
import com.erlang4j.tests.Erlang4JTestHelper;

public class MessageLexicalAnalyserTest extends TestCase {

	public void testLexical() {
		checkAnalyser("{abc,{Variable,def}}", //
				openTuple, "{", //
				atomName, "abc", //
				openTuple, "{", //
				variable, "Variable", //
				atomName, "def",// 
				closeTuple, "}", //
				closeTuple, "}");
		checkAnalyser("[abc,[Variable,def]]", //
				openList, "[", //
				atomName, "abc", //
				openList, "[", //
				variable, "Variable", //
				atomName, "def",// 
				closeList, "]", //
				closeList, "]");
		checkAnalyser("[a23,[Var1able,def]]", //
				openList, "[", //
				atomName, "a23", //
				openList, "[", //
				variable, "Var1able", //
				atomName, "def",// 
				closeList, "]", //
				closeList, "]");
		checkAnalyser("Any", variable, "Any");
	}

	public void testWithUnexpectedEnd() {
		checkUnexpectedEnd("{abc,", openTuple);
		checkUnexpectedEnd("{{abc,", openTuple, openTuple);
		checkUnexpectedEnd("{{abc,", openTuple, openTuple);
		checkUnexpectedEnd("[{abc},", openList);
		checkUnexpectedEnd("[{abc}{d[,", openList, openTuple, openList);
	}

	public void testWithEndTooSoon() {
		checkMalformed("abc}", "Unexpected closing token } at position 4 in string abc}");
		checkMalformed("{abc}]", "Unexpected closing token ] at position 6 in string {abc}]");
		checkMalformed("{abc}}", "Unexpected closing token } at position 6 in string {abc}}");
	}

	public void testWithWrongClose() {
		checkMalformed("[abc}", "Unexpected closing token } at position 5 in string [abc}");
		checkMalformed("[abc}]", "Unexpected closing token } at position 5 in string [abc}]");
		checkMalformed("{abc]}", "Unexpected closing token ] at position 5 in string {abc]}");
	}

	private void checkMalformed(final String string, String message) {
		Erlang4jMalformedPattern t = Erlang4JTestHelper.assertThrows(Erlang4jMalformedPattern.class, new Runnable() {
			public void run() {
				new MessageLexicalAnalyser(string);
			}
		});
		assertEquals(message, t.getMessage());

	}

	private void checkUnexpectedEnd(final String string, MessageTokenType... expected) {
		Erlang4jUnexpectedEndOfTokens t = Erlang4JTestHelper.assertThrows(Erlang4jUnexpectedEndOfTokens.class, new Runnable() {
			public void run() {
				new MessageLexicalAnalyser(string);
			}
		});
		Stack<MessageTokenType> actual = t.getExpected();
		assertEquals(Arrays.asList(expected), actual);
		assertEquals("Unexpected end of tokens. Still need to close " + Arrays.asList(expected) + " in string " + string, t.getMessage());

	}

	private void checkAnalyser(String string, Object... tokenTypeAndValues) {
		int i = 0;
		MessageLexicalAnalyser analyser = new MessageLexicalAnalyser(string);
		for (MessageToken token : analyser) {
			MessageTokenType expectedType = (MessageTokenType) tokenTypeAndValues[i + 0];
			String expectedValue = (String) tokenTypeAndValues[i + 1];
			assertEquals("i: " + i, expectedValue, token.value);
			assertEquals("i: " + i, expectedType, token.type);
			i += 2;
		}
	}
}
